#include "StdAfx.h"

#include "Lobby/GameSpy/CryGameSpyVoice.h"

#if USE_CRYLOBBY_GAMESPY && USE_CRYLOBBY_GAMESPY_VOIP

#include "Lobby/GameSpy/CryGameSpyLobby.h"
#include "Lobby/CrySharedLobbyPacket.h"

#pragma comment(lib, "dsound.lib")
#pragma comment(lib, "dxguid.lib")

#define MAX_VOICE_BUFFER_TIME	200
#define GAMESPY_VOICE_SEND_THRESHOLD ((7 * GAMESPY_VOICE_BUFFER_SIZE) / 10)

#define GAMESPY_LOCAL_USER (0)

CCryGameSpyVoice::CCryGameSpyVoice(CCryLobby* pLobby, CCryLobbyService* pService)
: CCryVoice(pLobby, pService)
, m_captureDevice(NULL)
, m_playbackDevice(NULL)
, m_deviceInfoCount(0)
, m_initialised(false)
{
	m_numLocalTalkers = MAX_GAMESPY_LOCAL_TALKERS;
	m_numRemoteTalkers = MAX_GAMESPY_REMOTE_TALKERS;

	// Make the CCryVoice base pointers point to our data so we can use the common code in CCryVoice
	for (uint32 i = 0; i < m_numRemoteTalkers; i++)
	{
		m_pRemoteUsers[i] = &m_remoteUsers[i];
	}

	for (uint32 i = 0; i < m_numLocalTalkers; i++)
	{
		for (uint32 j = 0; j < m_numRemoteTalkers; j++)
		{
			m_pUserRelationship[i][j] = &m_userRelationship[i][j];
		}
	}

	gEnv->pConsole->GetCVar("net_gamespy_voip_enable")->SetOnChangeCallback(VOIPEnabledChanged);
	gEnv->pConsole->GetCVar("net_gamespy_voip_ptt")->SetOnChangeCallback(VOIPPushToTalkChanged);
	gEnv->pConsole->GetCVar("net_gamespy_voip_playback_volume")->SetOnChangeCallback(VOIPPlaybackVolumeChanged);
}

ECryLobbyError CCryGameSpyVoice::Initialise()
{
	LOBBY_AUTO_LOCK;

	ECryLobbyError rc = PARENT::Initialise();
	
	if (rc == eCLE_Success)
	{
		m_localUser.m_bPushToTalkEnabled = false;
		m_localUser.m_bPushToTalkDown = false;
		m_localUser.m_playbackVolume = 0.5f;
	
		rc = InitDevices();

		VOIPEnabledChanged(NULL);
		VOIPPlaybackVolumeChanged(NULL);
		VOIPPushToTalkChanged(NULL);
	}

	return rc;
}

ECryLobbyError CCryGameSpyVoice::InitDevices()
{
	ECryLobbyError rc = eCLE_Success;

	if (m_initialised == false)
	{
		if (gvStartup(NULL) == GVTrue)
		{
			SConfigurationParams neededInfo[1] = { CLCC_GAMESPY_VOICE_CODEC, { NULL } };
			m_pLobby->GetConfigurationInformation(neededInfo, 1);

			m_pGameSpyVoiceCodecInfo = static_cast<SCryGameSpyVoiceCodecInfo*>(neededInfo[0].m_pData);
			if (m_pGameSpyVoiceCodecInfo != NULL)
			{
				if (m_pGameSpyVoiceCodecInfo->m_pCustomCodecInfo != NULL)
				{
					gvSetCustomCodec(static_cast<GVCustomCodecInfo*>(m_pGameSpyVoiceCodecInfo->m_pCustomCodecInfo));
				}

				m_initialised = true;
				m_deviceInfoCount = gvListDevices(m_deviceInfo, MAX_GAMESPY_VOIP_DEVICES, GV_CAPTURE_AND_PLAYBACK);

				NetLog("[Voice]: creating devices...");
				if (m_deviceInfoCount > 0)
				{
					for (uint32 index = 0; (index < m_deviceInfoCount) && ((m_captureDevice == NULL) || (m_playbackDevice == NULL)); ++index)
					{
						if ((m_captureDevice == NULL) && (m_deviceInfo[index].m_deviceType & GV_CAPTURE) && (m_deviceInfo[index].m_defaultDevice & GV_CAPTURE))
						{
							if (!CreateDevice(m_captureDevice, GV_CAPTURE, index))
							{
								rc = eCLE_InternalError;
							}
						}

						if ((m_playbackDevice == NULL) && (m_deviceInfo[index].m_deviceType & GV_PLAYBACK) && (m_deviceInfo[index].m_defaultDevice & GV_PLAYBACK))
						{
							if (!CreateDevice(m_playbackDevice, GV_PLAYBACK, index))
							{
								rc = eCLE_InternalError;
							}
						}
					}
				}

				if (m_captureDevice == NULL)
				{
					NetLog("[Voice]: no default capture device");
				}
				if (m_playbackDevice == NULL)
				{
					NetLog("[Voice]: no default playback device");
				}
			}
			else
			{
				rc = eCLE_InvalidParam;
			}
		}
		else
		{
			NetLog("[Voice]: gvStartup() failed");
		}
	}

	return rc;
}

ECryLobbyError CCryGameSpyVoice::Terminate()
{
	LOBBY_AUTO_LOCK;

	if (m_initialised)
	{
		FreeDevice(m_captureDevice, GV_CAPTURE);
		FreeDevice(m_playbackDevice, GV_PLAYBACK);

		if (m_pGameSpyVoiceCodecInfo->m_pTerminateCallback)
		{
			m_pGameSpyVoiceCodecInfo->m_pTerminateCallback();
		}

		gvCleanup();
		m_initialised = false;
	}

	return eCLE_Success;
}

void CCryGameSpyVoice::Tick(CTimeValue tv)
{
	if (m_initialised)
	{
		gvThink();

		uint32 tickTime = (uint32)(tv.GetMilliSecondsAsInt64() - m_lastTickTime.GetMilliSecondsAsInt64());
		int32 bytesToSend = CaptureVoiceData();

		if ((tickTime >= MAX_VOICE_BUFFER_TIME) || (bytesToSend >= GAMESPY_VOICE_SEND_THRESHOLD))
		{
			if (bytesToSend > 0)
			{
				SendVoiceData();
			}
		}

		if (((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_loopback)
		{
			if (m_localUser.m_numCapturedBytes > 0)
			{
				gvPlayPacket(m_playbackDevice, m_localUser.m_captureBuffer, m_localUser.m_numCapturedBytes, 0, m_localUser.m_frameStamp, GVFalse);
			}
		}

		for (CryVoiceRemoteUserID remoteUserIndex = 0; remoteUserIndex < m_numRemoteTalkers; ++remoteUserIndex)
		{
			SRemoteUser* pRemoteUser = &m_remoteUsers[remoteUserIndex];

			if (pRemoteUser->m_remoteUserFlags & UF_REGISTERED)
			{
				pRemoteUser->m_remoteUserFlags &= ~UF_IS_SPEAKING;
			}
		}

		CCryVoice::Tick(tv);
	}
}

void CCryGameSpyVoice::OnPacket(const TNetAddress& addr, CCryLobbyPacket* pPacket)
{
	CCrySharedLobbyPacket* pSPacket = (CCrySharedLobbyPacket*)pPacket;

	switch (pSPacket->StartRead())
	{
	case eGameSpyPT_Voice:
		ProcessVoiceData(addr, pSPacket);
		break;
	case eLobbyPT_VoiceMicrophoneNotification:
		ProcessMicrophoneNotification(addr, pPacket);
		break;
	case eLobbyPT_VoiceMicrophoneRequest:
		ProcessMicrophoneRequest(addr, pPacket);
		break;
	}
}

CryVoiceRemoteUserID CCryGameSpyVoice::RegisterRemoteUser(CryLobbyConnectionID connectionID, CryUserID remoteUserID)
{
	CryVoiceRemoteUserID userID = PARENT::RegisterRemoteUser(connectionID, remoteUserID);

	NetLog("[Voice]: registering remote user %i", userID);

	return userID;
}

void CCryGameSpyVoice::UnRegisterRemoteUser(CryLobbyConnectionID connectionID)
{
	for (uint32 index = 0; index < m_numRemoteTalkers; ++index)
	{
		SRemoteUser* pUser = &m_remoteUsers[index];

		if ((pUser->m_remoteUserFlags & UF_REGISTERED) && (pUser->m_connectionID == connectionID))
		{
			NetLog("[Voice]: unregistering remote user %i", index);

			PARENT::UnRegisterRemoteUser(index);
			break;
		}
	}
}

void CCryGameSpyVoice::LateSetUserLobbyConnection( CryLobbyConnectionID connectionID, CryUserID remoteUserID )
{
	CryVoiceRemoteUserID	id = GetRemoteUser( remoteUserID );

	if ( id != CryVoiceInvalidRemoteUserID )
	{
		SRemoteUser*					pRemoteUser = &m_remoteUsers[ id ];

		if ( pRemoteUser->m_connectionID == CryLobbyInvalidConnectionID )
		{
			pRemoteUser->m_connectionID = connectionID;
		}
	}
}

bool CCryGameSpyVoice::IsSpeaking(uint32 localUser)
{
	return (m_localUser.m_numCapturedBytes > 0) || (m_localUser.m_bPushToTalkEnabled && m_localUser.m_bPushToTalkDown);
}

bool CCryGameSpyVoice::IsMicrophoneConnected(CryUserID userID)
{
	bool connected = false;
	bool local = false;

	CryUserID localUserID = m_pService->GetUserID(0);
	if (localUserID == userID)
	{
		bool enabled = !(((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_enable == 0); 
		connected = (enabled && (m_captureDevice != NULL));
		bool thinkMicrophoneConnected = (m_localUser.m_localUserFlags & UF_IS_MICROPHONE_CONNECTED) ? true : false;

		if (thinkMicrophoneConnected != connected)
		{
			if (connected)
			{
				m_localUser.m_localUserFlags |= UF_IS_MICROPHONE_CONNECTED;
			}
			else
			{
				m_localUser.m_localUserFlags &= ~UF_IS_MICROPHONE_CONNECTED;
			}

			//-- Must tell all remote users about status change
			for (uint32 i = 0; i < m_numRemoteTalkers; i++)
			{
				SRemoteUser* pRemoteUser = &m_remoteUsers[i];
				if (pRemoteUser->m_remoteUserFlags & UF_REGISTERED)
				{
					pRemoteUser->m_remoteUserFlags |= UF_SEND_MICROPHONE_STATUS;

					//-- IMPORTANT - if net_gamespy_voip_enable is not enabled, setting the flags is NOT enough to send the microphone 
					//-- status to everyone because Tick() is no longer running.
					//-- THEREFORE, if enabled == false, we must call SendMicrophoneNotification directly from here.
					if (enabled == false)
					{
						SendMicrophoneNotification(i);
					}
				}
			}
		}

		local = true;
	}

	if (!local)
	{
		connected = CCryVoice::IsMicrophoneConnected(userID);
	}

	return connected;
}

void CCryGameSpyVoice::SendVoiceData(void)
{
	if (m_localUser.m_numCapturedBytes > 0)
	{
		const uint32 packetSize = CryLobbyPacketHeaderSize + CryLobbyPacketUINT16Size + CryLobbyPacketUINT16Size + GAMESPY_VOICE_BUFFER_SIZE;
		CCrySharedLobbyPacket packet;

		if (packet.CreateWriteBuffer(packetSize))
		{
			packet.StartWrite(eGameSpyPT_Voice, false);
			packet.WriteUINT16((uint16)m_localUser.m_numCapturedBytes);
			packet.WriteUINT16(m_localUser.m_frameStamp);
			packet.WriteData(m_localUser.m_captureBuffer, m_localUser.m_numCapturedBytes);

			for (uint32 index = 0; index < m_numRemoteTalkers; ++index)
			{
				SRemoteUser* pRemoteUser = &m_remoteUsers[index];
				SUserRelationship* pRelationship = &m_userRelationship[GAMESPY_LOCAL_USER][index];

				// Set to true if we want to send to this user
				if (pRemoteUser->m_remoteUserFlags & UF_REGISTERED)
				{
					if (((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_logging)
					{
						NetLog("[Voice]: Remote user %i (muted game %s, muted external %s)", index, ((pRelationship->m_relationshipFlags & RF_MUTED_GAME) ? "TRUE" : "FALSE"), ((pRelationship->m_relationshipFlags & RF_MUTED_EXTERNAL) ? "TRUE" : "FALSE"));
					}

					if (!(pRelationship->m_relationshipFlags & (RF_MUTED_GAME | RF_MUTED_EXTERNAL)))
					{
						TNetAddress to;

						if (m_pLobby->AddressFromConnection(to, pRemoteUser->m_connectionID))
						{
							if (((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_logging)
							{
								NetLog("[Voice]: Sending data to user %i", index);
							}

							m_pLobby->SendVoice(&packet, to);
						}
					}
				}
			}

			packet.FreeWriteBuffer();
		}
	}
}

void CCryGameSpyVoice::ProcessVoiceData(const TNetAddress& addr, CCrySharedLobbyPacket* pPacket)
{
	CryLobbyConnectionID connectionID;

	if (m_pLobby->ConnectionFromAddress(&connectionID, addr))
	{
		GVByte voiceData[GAMESPY_VOICE_BUFFER_SIZE];
		int32 voiceDataSize;
		GVFrameStamp voiceDataFrameStamp;

		pPacket->StartRead();
		voiceDataSize = pPacket->ReadUINT16();
		voiceDataFrameStamp = pPacket->ReadUINT16();
		pPacket->ReadData(voiceData, voiceDataSize);

		for (CryVoiceRemoteUserID remoteUserIndex = 0; remoteUserIndex < m_numRemoteTalkers; ++remoteUserIndex)
		{
			SRemoteUser* pRemoteUser = &m_remoteUsers[remoteUserIndex];

			if ((pRemoteUser->m_remoteUserFlags & UF_REGISTERED) && (pRemoteUser->m_connectionID == connectionID))
			{
				if (((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_logging)
				{
					NetLog("[Voice]: received data from user %i", remoteUserIndex);
				}

				pRemoteUser->m_remoteUserFlags |= UF_IS_SPEAKING;
				PlayVoiceData(voiceData, voiceDataSize, remoteUserIndex, voiceDataFrameStamp);
			}
		}
	}
}

bool CCryGameSpyVoice::CreateDevice(GVDevice& device, GVDeviceType deviceType, uint32 deviceInfoIndex)
{
	bool rc = false;
	if (deviceInfoIndex < m_deviceInfoCount)
	{
		FreeDevice(device, deviceType);
		device = gvNewDevice(m_deviceInfo[deviceInfoIndex].m_id, deviceType);
		if (device != NULL)
		{
			if (gvStartDevice(device, deviceType) == GVTrue)
			{
				NetLog("[Voice]: created%s%s device", ((deviceType & GV_CAPTURE) == GV_CAPTURE) ? " capture" : "", ((deviceType & GV_PLAYBACK) == GV_PLAYBACK) ? " playback" : "");
				rc = true;
			}
		}
	}

	return rc;
}

void CCryGameSpyVoice::FreeDevice(GVDevice& device, GVDeviceType deviceType)
{
	if (device != NULL)
	{
		gvStopDevice(device, deviceType);
		gvFreeDevice(device);
		device = NULL;
	}
}

uint32 CCryGameSpyVoice::CaptureVoiceData(void)
{
	int32 bytesAvailable = 0;

	if (m_captureDevice != NULL)
	{
		bytesAvailable = gvGetAvailableCaptureBytes(m_captureDevice);

		if (bytesAvailable > 0)
		{
			m_localUser.m_numCapturedBytes = GAMESPY_VOICE_BUFFER_SIZE;
			gvCapturePacket(m_captureDevice, m_localUser.m_captureBuffer, &m_localUser.m_numCapturedBytes, &m_localUser.m_frameStamp, &m_localUser.m_capturePeakVolume);

			if (m_localUser.m_bPushToTalkEnabled == false)
			{
				float threshold = ((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_threshold;
				if (m_localUser.m_capturePeakVolume < threshold)
				{
					bytesAvailable = 0;
					m_localUser.m_numCapturedBytes = 0;
				}
				else
				{
					if (((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_logging)
					{
						NetLog("[Voice]: captured %i bytes, frame %i, peak volume %.02f", m_localUser.m_numCapturedBytes, m_localUser.m_frameStamp, m_localUser.m_capturePeakVolume);
					}
				}
			}
			else
			{
				if (m_localUser.m_bPushToTalkDown == false)
				{
					bytesAvailable = 0;
					m_localUser.m_numCapturedBytes = 0;
				}
				else
				{
					if (((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_logging)
					{
						NetLog("[Voice]: captured %i bytes, frame %i, peak volume %.02f", m_localUser.m_numCapturedBytes, m_localUser.m_frameStamp, m_localUser.m_capturePeakVolume);
					}
				}
			}
		}
	}

	return bytesAvailable;
}

void CCryGameSpyVoice::PlayVoiceData(GVByte* pData, int32 dataSize, CryVoiceRemoteUserID userID, GVFrameStamp& frameStamp)
{
	if (m_playbackDevice != NULL)
	{
		SUserRelationship* pRelationship = &m_userRelationship[GAMESPY_LOCAL_USER][userID];
		GVBool muted = GVTrue;
		if (!(pRelationship->m_relationshipFlags & (RF_MUTED_GAME | RF_MUTED_EXTERNAL)))
		{
			muted = GVFalse;
		}

		if (((CCryGameSpyLobbyService*)m_pService)->net_gamespy_voip_logging)
		{
			NetLog("[Voice]: playback from user %i, frame %i, muted %s", userID, frameStamp, (muted == GVTrue) ? "YES" : "NO");
		}

		gvPlayPacket(m_playbackDevice, pData, dataSize, userID, frameStamp, muted);
	}
}

void CCryGameSpyVoice::VOIPEnabledChanged(ICVar* pCVar)
{
	CCryGameSpyVoice* pThis = static_cast<CCryGameSpyVoice*>(CCryLobby::GetLobby()->GetVoice());

	if (pThis != NULL)
	{
		bool enabled = !(((CCryGameSpyLobbyService*)pThis->m_pService)->net_gamespy_voip_enable == 0); 
		if (enabled)
		{
			if (!(pThis->m_initialised))
			{
				pThis->InitDevices();
			}
		}
		else
		{
			if (pThis->m_initialised)
			{
				pThis->Terminate();
			}
		}
	}
}

void CCryGameSpyVoice::VOIPPushToTalkChanged(ICVar* pCVar)
{
	CCryGameSpyVoice* pThis = static_cast<CCryGameSpyVoice*>(CCryLobby::GetLobby()->GetVoice());

	if (pThis != NULL)
	{
		bool ptt = !(((CCryGameSpyLobbyService*)pThis->m_pService)->net_gamespy_voip_ptt == 0); 
		if (ptt)
		{
			if (pThis->m_initialised)
			{
				pThis->EnablePushToTalk(0, true);
			}
		}
		else
		{
			if (pThis->m_initialised)
			{
				pThis->EnablePushToTalk(0, false);
			}
		}
	}
}

void CCryGameSpyVoice::VOIPPlaybackVolumeChanged(ICVar* pCVar)
{
	CCryGameSpyVoice* pThis = static_cast<CCryGameSpyVoice*>(CCryLobby::GetLobby()->GetVoice());

	if (pThis != NULL)
	{
		if (pThis->m_initialised)
		{
			pThis->SetVoicePlaybackVolume(0, ((CCryGameSpyLobbyService*)pThis->m_pService)->net_gamespy_voip_playback_volume);
		}
	}
}

void CCryGameSpyVoice::SendMicrophoneNotification(uint32 remoteUserID)
{
	SRemoteUser* pRemoteUser = &m_remoteUsers[remoteUserID];
	const uint32 bufferSize = CryLobbyPacketHeaderSize + CryLobbyPacketUINT8Size;
	CCryLobbyPacket packet;

	if (packet.CreateWriteBuffer(bufferSize))
	{
		packet.StartWrite(eLobbyPT_VoiceMicrophoneNotification, false);

		uint8 flags = ((m_localUser.m_localUserFlags & UF_IS_MICROPHONE_CONNECTED) == UF_IS_MICROPHONE_CONNECTED) ? 1 : 0;
		packet.WriteUINT8(flags);

		TNetAddress to;
		if (m_pLobby->AddressFromConnection(to, pRemoteUser->m_connectionID))
		{
			m_pLobby->Send(&packet, to, CryLobbyInvalidConnectionID, NULL);

			//-- Have sent the status to this user, so we can clear the send flag
			pRemoteUser->m_remoteUserFlags &= ~UF_SEND_MICROPHONE_STATUS;

#if NET_MICROPHONE_DEBUG 
			NetLog("[mic] Sending Microphone Notification to remote user %d (connectionID %d): write flags 0x%x",
				remoteUserID,
				pRemoteUser->m_connectionID,
				flags);
#endif // #if NET_MICROPHONE_DEBUG 
		}
	}
}

void CCryGameSpyVoice::ProcessMicrophoneNotification(const TNetAddress& addr, CCryLobbyPacket* pPacket)
{
	CCryGameSpyMatchMaking* pMatchMaking = (CCryGameSpyMatchMaking*)m_pService->GetMatchMaking();
	if (pMatchMaking)
	{
		CryLobbyConnectionID connectionID;

		if (m_pLobby->ConnectionFromAddress(&connectionID, addr))
		{
			uint8 flags;

			pPacket->StartRead();
			flags = pPacket->ReadUINT8();
#if NET_MICROPHONE_DEBUG 
			NetLog("[mic] Received Microphone Notification: read flags 0x%x", flags);
#endif // #if NET_MICROPHONE_DEBUG 

			for (uint32 remoteUserIndex = 0; remoteUserIndex < m_numRemoteTalkers; ++remoteUserIndex)
			{
				SRemoteUser* pRemoteUser = &m_remoteUsers[remoteUserIndex];
				if ((pRemoteUser->m_remoteUserFlags & UF_REGISTERED) && pRemoteUser->m_connectionID == connectionID)
				{
					if (flags != 0)
					{
						pRemoteUser->m_remoteUserFlags |= UF_IS_MICROPHONE_CONNECTED;
					}
					else
					{
						pRemoteUser->m_remoteUserFlags &= ~UF_IS_MICROPHONE_CONNECTED;
					}

					// no longer need to request status from this user any more, since it has received at least one microphone status packet from this user
					pRemoteUser->m_remoteUserFlags &= ~UF_REQUEST_MICROPHONE_STATUS;

#if NET_MICROPHONE_DEBUG 
					NetLog("[mic] for remote user %i, state %i", remoteUserIndex, (pRemoteUser->m_remoteUserFlags & UF_IS_MICROPHONE_CONNECTED) ? 1 : 0);
#endif // #if NET_MICROPHONE_DEBUG 
					return;
				}
			}
		}
	}
}

void CCryGameSpyVoice::SendMicrophoneRequest(uint32 remoteUserID)
{
	SRemoteUser* pRemoteUser = &m_remoteUsers[remoteUserID];
	const uint32 bufferSize = CryLobbyPacketHeaderSize;
	CCryLobbyPacket packet;

	if (packet.CreateWriteBuffer(bufferSize))
	{
		packet.StartWrite(eLobbyPT_VoiceMicrophoneRequest, false);

		TNetAddress to;
		if (m_pLobby->AddressFromConnection(to, pRemoteUser->m_connectionID))
		{
			m_pLobby->Send(&packet, to, CryLobbyInvalidConnectionID, NULL);

			//-- Have sent the status to this user, so we can clear the send flag
			pRemoteUser->m_remoteUserFlags &= ~UF_SEND_MICROPHONE_STATUS;

#if NET_MICROPHONE_DEBUG 
			NetLog("[mic] Sending Microphone Request to remote user %d (connectionID %d)",
				remoteUserID,
				pRemoteUser->m_connectionID);
#endif // #if NET_MICROPHONE_DEBUG 
		}
	}
}

void CCryGameSpyVoice::ProcessMicrophoneRequest(const TNetAddress& addr, CCryLobbyPacket* pPacket)
{
	//-- received a request for microphone status
#if NET_MICROPHONE_DEBUG 
	NetLog("[mic] Received Microphone Request");
#endif // #if NET_MICROPHONE_DEBUG 

	CCryGameSpyMatchMaking* pMatchMaking = (CCryGameSpyMatchMaking*)m_pService->GetMatchMaking();
	if (pMatchMaking)
	{
		CryLobbyConnectionID connectionID;
		if (m_pLobby->ConnectionFromAddress(&connectionID, addr))
		{
			for (uint32 remoteUserIndex = 0; remoteUserIndex < m_numRemoteTalkers; ++remoteUserIndex)
			{
				SRemoteUser* pRemoteUser = &m_remoteUsers[remoteUserIndex];
				if ((pRemoteUser->m_remoteUserFlags & UF_REGISTERED) && pRemoteUser->m_connectionID == connectionID)
				{
					//-- flag sending status out to this remote user
					pRemoteUser->m_remoteUserFlags |= UF_SEND_MICROPHONE_STATUS;

#if NET_MICROPHONE_DEBUG 
					NetLog("[mic] from remote user %i", remoteUserIndex);
#endif // #if NET_MICROPHONE_DEBUG 
					return;
				}
			}
		}
	}
}


void CCryGameSpyVoice::EnablePushToTalk(uint32 localUser, bool bSet)
{
	m_localUser.m_bPushToTalkEnabled = bSet;
}

bool CCryGameSpyVoice::IsPushToTalkEnabled(uint32 localUser)
{
	return m_localUser.m_bPushToTalkEnabled;
}

void CCryGameSpyVoice::PushToTalkButtonDown(uint32 localUser)
{
	m_localUser.m_bPushToTalkDown = true;
}

void CCryGameSpyVoice::PushToTalkButtonUp(uint32 localUser)
{
	m_localUser.m_bPushToTalkDown = false;
}

void CCryGameSpyVoice::SetVoicePlaybackVolume(uint32 localUser, float volume)
{
	if (m_playbackDevice != NULL)
	{
		gvSetDeviceVolume(m_playbackDevice, GV_PLAYBACK, volume);
	}
	m_localUser.m_playbackVolume = volume;
}

float CCryGameSpyVoice::GetVoicePlaybackVolume(uint32 localUser)
{
	return (float)m_localUser.m_playbackVolume;
}



#endif // USE_CRYLOBBY_GAMESPY && USE_CRYLOBBY_GAMESPY_VOIP
// [EOF]
